#include <exceptdef.h>
#include <construt.hpp>

#include "misc.h" // siof_args

/**
 * @brief StackInfo
 *  函数传参和返回值的过程可以看作 栈push pop ，题目要求模拟这一过程
 *  实现思路：
 *  函数传参的时候，参数会被push 到 栈中，使用stackInfo 模拟这一过程有一个缺失的信息，
 *  不知道栈需要开辟多大（除非使用链表这种数据结构），所以makeStackInfo<argsType> ，来“模拟编译器开辟函数栈帧这一过程”
 *  传出参数的顺序，要和pop的顺序相反。
 */

class StackInfo {
private:
    char *_argBegin; // 函数参数开始位置
    char *_argEnd;   // 结束位置
    char *_arg;      // 使用参数位置
public:
    StackInfo(StackInfo &&other) {
        _argBegin = other._argBegin;
        _argEnd = other._argEnd;
        _arg = other._arg;
        other._argBegin = nullptr;
    }
    StackInfo &operator=(StackInfo &&other) {
        _argBegin = other._argBegin;
        _argEnd = other._argEnd;
        _arg = other._arg;
        other._argBegin = nullptr;
    }

    template <typename Ret, typename... Args>
    static StackInfo makeStackInfo() {
        size_t retSize = std::is_same_v<void, Ret> ? 0 : sizeof(Ret);
        return {std::max(retSize, sizeof_args<Args...>)};
    }

    ~StackInfo() {
        //  如果StackInfo没有清空，存储的元素会因为没有析构而导致内存泄漏
        ::operator delete(_argBegin);
        _argBegin = nullptr;
        _argEnd = nullptr;
        _arg = nullptr;
    }

    // posh 的顺序，需要和pop 顺序对应上

    template <typename T>
    void push(T &&arg) {
        // TING_DEBUG("[StackInfo] cost memory: %zd",sizeof(T));
        TING_ASSERT(_arg < _argEnd, "StackInfo already  full!!");
        using NoRefType = std::remove_reference_t<T>;
        TingSTL::construct((NoRefType *)_arg, NoRefType(std::forward<T>(arg)));
        _arg += sizeof(T);
    }

    template <typename T>
    T pop() {
        // TING_DEBUG("[StackInfo] : repay  memory %zd", sizeof(T));

        _arg -= sizeof(T);
        TING_ASSERT(_arg >= _argBegin, "StackInfo pop  error!!");
        auto ret = std::move(*((T *)_arg));
        TingSTL::destroy((T *)_arg);
        return ret;
    }

private:
    /**
     * @brief Construct a new Stack Info object
     *
     * @param sizeOfArgs 返回 参数所占空间
     */
    StackInfo(size_t sizeOfArgs) {
        // TING_DEBUG("[StackInfo] sizeOfArgs: %zd",sizeOfArgs);

        _argBegin = (char *)::operator new(sizeOfArgs);
        _arg = _argBegin;
        _argEnd = _argBegin + sizeOfArgs;
    }
};
